3 Simple Steps to Send Email Alerts From a Linux Server

 

In this tutorial, you will configure a Linux server to send Email through Gmail. It’s much easier than setting up a full-featured Email server and is more appropriate if all you need is to get Email alerts out from your server or application.

I’ve also included a quick script at the end of this tutorial that will check the status of systemd periodically and send out Email alerts if any problems are detected.

This guide assumes you are using Debian/Ubuntu, some commands and package names may need to be adapted to your OS if you are not.

Step 1 - Set up an Email account

In this example I am using Gmail because it is a popular choice, other mail services will work in a similar manner. You will need to enable the “less secure apps” setting in Gmail to allow your server to log in and send mail without getting blocked by Google’s security measures, visit https://myaccount.google.com/lesssecureapps, make sure you have the correct account selected in the top right corner, and then turn the feature on.

Step 2 - Configure Postfix

We will install the following packages:

apt install -y postfix mailutils libsasl2-modules

For CentOS, use yum install -y postfix mailx cyrus-sasl-plugin

Postfix will need to log in through Gmail to submit messages, you’ll need to create the sasl_passwd file and add your credentials to it.

/etc/postfix/sasl_passwd

[smtp.gmail.com]:587 username@gmail.com:password

The postmap command will convert our text file into a lookup table, which is a data format that postfix can quickly and efficiently parse for data.

postmap /etc/postfix/sasl_passwd

Since these two files contain your Gmail password, it is wise to lock down their permissions so only the root user can access their contents.

chown root. /etc/postfix/sasl_passwd

chmod 600 /etc/postfix/sasl_passwd

chown root. /etc/postfix/sasl_passwd.db

chmod 600 /etc/postfix/sasl_passwd.db

the chown command changes the owner user and group, and the chmod command will changes the access mode (permissions), more detailed information on ownership and permissions can be found here.

Using chown root. is the same as chown root:root or chown root.root, leaving the group name blank will use the default group for the user specified.

We need to set up aliases and virtual alias maps, aliases will redirect mail from one user to another on the same host, for example from postmaster@ServerHostName to root@ServerHostName, we will direct all Email to root here. Virtual alias maps complete the picture by redirecting mail from local users on this host to an external Email address.

How to Home Lab: Part 5 - Secure SSH Remote Access

/etc/aliases

You should run the command newaliases after changing this file so it will be parsed appropriately. You can add any other local users that may receive mail to this list.

postmaster: root

nobody: root

hostmaster: root

webmaster: root

www: root

/etc/postfix/virtual

The Email address here is where any mail will be directed.

root you@yourdomain.com

The virtual alias map file also needs to be post mapped.

postmap /etc/postfix/virtual

In the main configuration file for postfix, update the values for the following lines, if a line is missing from your configuration file you’ll just need to add the whole line.

/etc/postfix/main.cf

These settings tell postfix to use Gmail as a relay to send mail out, and then specify the parameters and credentials for connecting to Gmail.

relayhost = [smtp.gmail.com]:587

smtp_tls_security_level = may

smtp_sasl_auth_enable = yes

smtp_sasl_security_options =

smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd

These settings are for aliasing as explained above

alias_maps = hash:/etc/aliases

alias_database = hash:/etc/aliases

virtual_alias_maps = hash:/etc/postfix/virtual

I also remove all the values from mydestination, this tells postfix it is not to receive any mail on this system, it will only send it out the relay host or reject the message.

mydestination =

If you do not have IPv6 enabled, you will need to specify the following value in main.cf as well, often DNS queries for Gmail will return the IPv6 address first, and postfix will fail to send mail because the relay host is unreachable.

inet_protocols = ipv4

Restart postfix to apply the configuration.

systemctl restart postfix

Step 3 - Make sure it all works

Now you should be able to send a test Email with the command below, we will send the email to the root user and it should get re-routed to the final address specified in /etc/postfix/virtual.

echo "This is a test Email" | mail -s "Testing" root

Bring up the logs for postfix with the command below, then hold the shift key and press the letter g on your keyboard to jump to the bottom where the newest entries are.

less /var/log/mail.log

If everything went smoothly, you should see log entries similar to those below.

postfix/pickup[110202]: 4317C18E47: uid=0 from=<root>

postfix/cleanup[62865]: 4317C18E47: message-id=<20190805104206.4317C18E47@ServerHostName>

postfix/qmgr[5024]: 4317C18E47: from=<root@ServerHostName>, size=249104, nrcpt=1 (queue active)

postfix/smtp[62876]: 4317C18E47: to=<you@yourdomain.com>, relay=smtp.gmail.com[74.125.195.109]:587, delay=1.3, delays=0.04/0.01/0.02/1.2, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 5B6D12E007B)

postfix/qmgr[5024]: 4317C18E47: removed

If you are setting this up on a VPS, you may see an error message like the one below. In which case you just need to log in to the Gmail account from your web browser, you should see an alert there stating that a login attempt was blocked; click through and choose “This was me”, and test sending mail again after that.

postfix/smtp[19724]: E3C71A4D: to=<you@yourdomain.com>, orig_to=<root>, relay=smtp.gmail.com[108.177.9.108]:587, delay=202, delays=202/0.05/0.31/0, dsn=4.7.14, status=deferred (SASL authentication failed; server smtp.gmail.com[108.177.9.108] said: 534-5.7.14 <https://accounts.google.com/signin/continue?sarp=1&scc=1&plt=AKgnsbv6?534-5.7.14 Y_y6_l6lnq7awbD3g-kRUqe5uT5hbCPVkxC3L57wTzokPWquZVVw4B9Hk9dapPHmFWHhp5?534-5.7.14 9UOBPafiJZBzXH36l0MyenRtgeMtrMdv1Wxt1W2wEepiy1iB6thU3uuYsWqBzI> Please?534-5.7.14 log in via your web browser and then try again.?534-5.7.14  Learn more at?534 5.7.14  https://support.google.com/mail/answer/78754 q24sm27754378otl.31 - gsmtp)

How to Home Lab: Part 7 - Log Management

Bonus - Systemd monitoring script

This script will check the status of systemd, if all is well it just quits, if not it will fire off an Email alert specifying which unit has failed, and re-check every minute until the issue is resolved (without sending additional Emails until all services are restored).

Further Reading: The book Linux Command Line and Shell Scripting Bible, by Richard Blum, is a fantastic reference when learning how to write bash shell scripts because it is very easy to follow. Available on Amazon.

First, we need to create a file for the script and make it executable.

touch /usr/local/bin/systemd-failed-notifier.sh

chmod +x /usr/local/bin/systemd-failed-notifier.sh

Copy and paste the script below into that file.

/usr/local/bin/systemd-failed-notifier.sh

#!/bin/bash

 

emailAddress="root"

statusFile="/tmp/systemd-failed-notifier-status"

lockFile="/tmp/systemd-failed-notifier-lock"

 

# Check for lock file and create one or quit

if [ -f "$lockFile" ]; then

  exit 0

else

  touch "$lockFile"

fi

 

# Read status file if it exists or create it

if [ -f "$statusFile" ]; then

  source "$statusFile"

else

  touch "$statusFile"

  lastSystemStatus="unknown"

fi

 

doAlert () {

  # If system status has changed, update statusFile and send email

  if [ ! "$systemStatus" = "$lastSystemStatus" ]; then

    if [ "$systemStatus" = "degraded" ]; then

      failedUnits="$(systemctl --failed | grep failed | cut -f2 -d' ')"

    else

      failedUnits=""

    fi

    echo -e \

    "

    Current Status: $systemStatus

    Previous Status: $lastSystemStatus

 

    Failed Units:

    $failedUnits" | \

    mail -s "$(hostname) $systemStatus" $emailAddress

    sleep 5

    lastSystemStatus="$systemStatus"

  fi

}

 

# Run once

systemStatus=$(systemctl is-system-running)

doAlert

 

# Loop if system status is not running

while [ ! "$systemStatus" = "running" ]; do

  sleep 60

  systemStatus=$(systemctl is-system-running)

  doAlert

done

 

# Cleanup and exit

echo lastSystemStatus="$systemStatus" > "$statusFile"

rm "$lockFile"

exit 0

We just need to create the service and timer files for systemd to run the script periodically.

touch /etc/systemd/system/systemd-failed-notifier.service

touch /etc/systemd/system/systemd-failed-notifier.timer

/etc/systemd/system/systemd-failed-notifier.service

[Unit]

Description= Systemd status email alert service

 

[Service]

Type=simple

ExecStart=/usr/local/bin/systemd-failed-notifier.sh

/etc/systemd/system/systemd-failed-notifier.timer

The value OnBootSec sets the time to wait after the system boots up before running the script for the first time, and OnUnitActiveSec sets the time to wait in between each subsequent run of the script after that.

[Unit]

Description=Systemd status email alert timer

 

[Timer]

OnBootSec=5min

OnUnitActiveSec=60min

Unit=systemd-failed-notifier.service

 

[Install]

WantedBy=timers.target

The following commands will configure the system to start the timer every time it boots up, and to start the timer right now.

systemctl enable systemd-failed-notifier.timer

systemctl start systemd-failed-notifier.timer